package com.atguigu.gmall.order.service;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.common.util.HttpClientUtil;
import com.atguigu.gmall.model.enums.OrderStatus;
import com.atguigu.gmall.model.enums.ProcessStatus;
import com.atguigu.gmall.model.order.OrderDetail;
import com.atguigu.gmall.model.order.OrderInfo;
import com.atguigu.gmall.order.mapper.OrderDetailMapper;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class OrderInfoServiceImpl implements OrderInfoService {
    @Value("${ware.url}")
    private String wareUrl;
    @Autowired
    private ProductFeignClient productFeignClient;
    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private OrderInfoSyncService orderInfoSyncService;
    @Autowired
    private RabbitService rabbitService;

    //校验库存
    @Override
    public Boolean hasStock(OrderDetail orderDetail) {
        return "1".equals(
                HttpClientUtil.doGet(wareUrl + "/hasStock?skuId=" +
                        orderDetail.getSkuId() + "&num=" + orderDetail.getSkuNum()));
    }

    //3.提交订单 (保存订单主表 订单详情表)
    @Override
    public Long insertOrder(OrderInfo orderInfo) {
        //订单状态 给买家看的
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());//英文
        //orderInfo.setOrderStatus(OrderStatus.UNPAID.getComment());//中文
        //订单交易号 第三方支付用的
        String outTradeNo = "ATGUIGU" + System.currentTimeMillis() + new Random().nextInt(1000);
        orderInfo.setOutTradeNo(outTradeNo);
        //订单描述 扫完码手机显示的
        StringBuilder tradeBody = new StringBuilder();
        //防止页面篡改商品单价或总价 重新查询DB 篡改购买数量也没问题
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        if (!CollectionUtils.isEmpty(orderDetailList)) {
            for (OrderDetail orderDetail : orderDetailList) {
                BigDecimal price = productFeignClient.getPrice(orderDetail.getSkuId());
                orderDetail.setOrderPrice(price);
                //拼接订单描述
                tradeBody.append(orderDetail.getSkuName() + " ");
            }
        }
        orderInfo.sumTotalAmount();
        //校验长度  页面最大长度100
        if (tradeBody.length() > 100) {
            //substring返回值类型String
            orderInfo.setTradeBody(tradeBody.substring(0, 100));
        } else {
            orderInfo.setTradeBody(tradeBody.toString());
        }
        //创建时间
        Calendar calendar = Calendar.getInstance();
        orderInfo.setCreateTime(calendar.getTime());
        //失效时间
        calendar.add(Calendar.HOUR, 2);
        orderInfo.setExpireTime(calendar.getTime());
        //进度状态 给工作人员看的
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());
        //保存订单主表 因为表关联 必须先有订单主表
        orderInfoMapper.insert(orderInfo);
        List<String> hks = new ArrayList<>(orderDetailList.size());
        List<Long> skuIdList = new ArrayList<>(orderDetailList.size());
        //保存订单子表 每种商品一条记录 如何优化?
        orderDetailList.forEach(orderDetail -> {
            //外键
            orderDetail.setOrderId(orderInfo.getId());
            orderDetailMapper.insert(orderDetail);
            //供Redis删除使用
            hks.add(orderDetail.getSkuId().toString());
            //供DB删除使用 SQL要尽最大可能使用上索引
            skuIdList.add(orderDetail.getSkuId());
        });

        //4:清除下过订单的购物车 双写  DB不重要可以异步 出错打日志人工处理
        String cacheHKey = RedisConst.USER_KEY_PREFIX + orderInfo.getUserId() + RedisConst.USER_CART_KEY_SUFFIX;
        //批量删除
        redisTemplate.opsForHash().delete(cacheHKey, hks.toArray());
        //异步删除DB
        orderInfoSyncService.deleteOrderInfo(orderInfo.getUserId(), skuIdList);
        //5:开始计时 规定时间不付钱 取消订单
        //实质为自己发给自己 订单微服务发给订单微服务
        rabbitService.sendDelayedMessage(MqConst.EXCHANGE_DIRECT_ORDER_CANCEL,
                MqConst.ROUTING_ORDER_CANCEL, orderInfo.getId(), MqConst.DELAY_TIME);
        return orderInfo.getId();
    }

    //页面实质需要的订单id和订单金额 //不应该这么做
    @Override
    public OrderInfo getOrderInfo(Long orderId) {
        return orderInfoMapper.selectById(orderId);
    }

    //支付成功后 修改订单状态
    @Override
    public void updateOrderStatus(Long orderId, ProcessStatus processStatus) {
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setId(orderId);
        //先只设置订单状态 这里与进程状态无关
        orderInfo.setOrderStatus(processStatus.getOrderStatus().name());
        orderInfoMapper.updateById(orderInfo);
    }

    //初始化仓库需要的数据
    @Override
    public Map initWareOrder(Long orderId) {
        OrderInfo orderInfo = getOrderInfoAndDetails(orderId);
        Map map = initWareOrder(orderInfo);
        return map;
    }

    //初始化仓库需要的数据 重载
    public Map initWareOrder(OrderInfo orderInfo) {
        Map map = new HashMap<>();
        map.put("orderId", orderInfo.getId());
        map.put("consignee", orderInfo.getConsignee());
        map.put("consigneeTel", orderInfo.getConsigneeTel());
        map.put("orderComment", orderInfo.getOrderComment());
        map.put("orderBody", orderInfo.getTradeBody());
        map.put("deliveryAddress", orderInfo.getDeliveryAddress());
        map.put("paymentWay", orderInfo.getPaymentWay().equals("ONLINE") ? "2" : "1");
        map.put("wareId", orderInfo.getWareId());
        List<Map> details = orderInfo.getOrderDetailList().stream().map(orderDetail -> {
            Map m = new HashMap<>();
            m.put("skuId", orderDetail.getSkuId());
            m.put("skuNum", orderDetail.getSkuNum());
            m.put("skuName", orderDetail.getSkuName());
            return m;
        }).collect(Collectors.toList());
        map.put("details", details);
        return map;
    }

    //获取订单及订单详情集合
    public OrderInfo getOrderInfoAndDetails(Long orderId) {
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        if (null != orderInfo) {
            orderInfo.setOrderDetailList(orderDetailMapper.selectList(new QueryWrapper<OrderDetail>().
                    eq("order_id", orderId)));
        }
        return orderInfo;
    }

    //开始拆单 超级难  一张变N张
    @Override
    public List<OrderInfo> orderSplit(Long orderId, String wareSkuMap) {
        //父订单
        OrderInfo orderInfoOrigin = getOrderInfoAndDetails(orderId);
        // wareSkuMap格式 以下面为例
        //[{"wareId":"1","skuIds":["2","10"]},{"wareId":"2","skuIds":["3"]}]
        //表示：sku为2号，10号的商品在1号仓库  sku为3号商品在2号仓库
        List<Map> wareSkuMapList = JSONObject.parseArray(wareSkuMap, Map.class);
        //N多个子订单 List<Map>->List<OrderInfo>
        List<OrderInfo> subOrderInfoList = wareSkuMapList.stream().map(map -> {
            OrderInfo subOrderInfo = new OrderInfo();
            //属性复制
            BeanUtils.copyProperties(orderInfoOrigin, subOrderInfo);
            //子订单跟父订单的区别
            //1.ID不同 由于复制了父订单 再置为null 由DB生成
            subOrderInfo.setId(null);
            //2.子订单包含字段parent_order_id
            subOrderInfo.setParentOrderId(orderInfoOrigin.getId());
            //仓库id 子订单里才有仓库id
            Object wareId = map.get("wareId");
            subOrderInfo.setWareId(String.valueOf(wareId));
            //3.更新订单详情表外键 改为对应的新的子订单的主键
            //每个map格式如: {"wareId":"1","skuIds":["2","10"]} 如 {"wareId":"2","skuIds":["3"]}
            //比如这个skuIds 如果是1号仓库长度是2,如果是2号仓库 长度是1
            List<String> skuIds = (List<String>) map.get("skuIds");
            //获取父订单对应的订单详情 这个订单详情按照上方例子长度是3 分别是 2号3号10号商品的详情
            List<OrderDetail> orderDetailList = orderInfoOrigin.getOrderDetailList();
            //根据skuId 获得对应的子订单的订单详情
            List<OrderDetail> subOrderDetailList = orderDetailList.stream().filter(orderDetail -> {
                //skuId为Long类型
                if (skuIds.contains(String.valueOf(orderDetail.getSkuId()))) {
                    return true;
                } else {
                    return false;
                }
            }).collect(Collectors.toList());
            //子订单设置子订单详情
            subOrderInfo.setOrderDetailList(subOrderDetailList);
            //3.子订单设置金额
            subOrderInfo.sumTotalAmount();
            //保存子订单到DB 返回的是子订单的id
            orderInfoMapper.insert(subOrderInfo);
            //设置某个子订单对应的订单详情的外键 为子订单的id
            subOrderDetailList.forEach(subOrderDetail -> {
                subOrderDetail.setOrderId(subOrderInfo.getId());
                orderDetailMapper.updateById(subOrderDetail);
            });
            return subOrderInfo;
        }).collect(Collectors.toList());
        //父订单状态 改为已拆单
        updateOrderStatus(orderInfoOrigin.getId(), ProcessStatus.SPLIT);
        //返回子订单集合
        return subOrderInfoList;
    }
}
